package com.casa.POI.dianping;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.StatelessSession;
import org.hibernate.criterion.Property;
import org.htmlcleaner.TagNode;
import org.htmlcleaner.XPatherException;

public class ExtractShopInfoExecutor extends CityExecutor {
	
	public ExtractShopInfoExecutor(int cityId) {
		super(cityId);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void execute() {
		StatelessSession s = SessionManager.getInstance().openStatelessSession();
		while (true) {
			Criteria c = s.createCriteria(ShopData.class);
			c.add(Property.forName("cityId").eq(getCityId()));
			c.add(Property.forName("extracted").eq(0));
			c.setMaxResults(ConfigurationManager.getInstance().getBatchSize());
			List<ShopData> results = c.list();
			if (results.size() == 0)
				break;
			
			s.beginTransaction();
			for (ShopData shopData : results) {
				Shop shop = new Shop();
				shop.setUrl(shopData.getUrl());
				
				try {
					TagNode tn = extractTagNode(shopData);
					extractShop(tn, shop);
				} catch (Exception e) {
					e.printStackTrace();
					return;
				}
				
				shopData.setExtracted(1);
				s.update(shopData);
				s.insert(shop);
				
				printStatus("extract " + shopData.getUrl());
			}
			s.getTransaction().commit();
		}
		s.close();
	}
	
	private Location decode(String source) {
		int i = -1;
		int h = 0;
		StringBuilder sb = new StringBuilder();
		int j = source.length() - 1;
		int g = source.charAt(j);
		source = source.substring(0, j);
		for(int e = 0; e < j; e++) {
			int d = Integer.parseInt(String.valueOf(source.charAt(e)), 36) - 10;
			if(d >= 10) {
				d = d - 7;
			}
			sb.append(Integer.toString(d, 36));
			if(d > h) {
				i = e;
				h = d;
			}
		}
		int a = Integer.parseInt(sb.substring(0, i), 16);
		int f = Integer.parseInt(sb.substring(i + 1), 16);
		double lng = (a + f - g) / 2;
		double lat = (f - lng) / 100000f;
		lng /= 100000;
		return new Location(lat, lng);
	}
	
	private void extractShop(TagNode html, Shop shop) throws XPatherException {
		Object[] results = html.evaluateXPath("//div[@class='shop-info-inner Fix']");
		
		if (results.length > 0) {
			shop.setCityId(getCityId());
			TagNode shopInfoNode = (TagNode) results[0];
			extractName(shopInfoNode, shop);
			extractMark(shopInfoNode, shop);
			extractTotalComment(shopInfoNode, shop);
			extractAvgPrice(shopInfoNode, shop);
			extractPhone(shopInfoNode, shop);
			extractDistrict(shopInfoNode, shop);
			extractAddress(shopInfoNode, shop);
			extractImageUrl(shopInfoNode, shop);
			extractTags(shopInfoNode, shop);
			extractBranchUrl(html, shop);
			extractLocation(html, shop);
		}
	}
	
	private void extractTags(TagNode shopInfoNode, Shop shop) throws XPatherException {
		List<String> categories = new ArrayList<String>();
		List<String> categoryUrls = new ArrayList<String>();
		List<String> regions = new ArrayList<String>();
		List<String> regionUrls = new ArrayList<String>();
		
		Object[] results = shopInfoNode.evaluateXPath("//div[@class'desc-list']/dl[last()]/dd/span/a");
		
		for (Object result : results) {
			TagNode tn = (TagNode) result;
			String href = tn.getAttributeByName("href");
			String text = tn.getText().toString();
			int index = href.lastIndexOf('/');
			char c = href.charAt(index + 1);
			if (c == 'g') {
				categories.add(text);
				categoryUrls.add(href);
			} else if (c == 'r') {
				regions.add(text);
				regionUrls.add(href);
			}
		}
		
		if (categories.size() > 0) {
			String category = StringUtils.join(categories, ':');
			String categoryUrl = StringUtils.join(categoryUrls, ':');
			shop.setCategory(category);
			shop.setCategoryUrl(categoryUrl);
		}
		if (regions.size() > 0) {
			String region = StringUtils.join(regions, ':');
			String regionUrl = StringUtils.join(regionUrls, ':');
			shop.setRegion(region);
			shop.setRegionUrl(regionUrl);
		}
	}

	private void extractLocation(TagNode html, Shop shop) throws XPatherException {
		Object[] results = html.evaluateXPath("/body/script");
		
		for (Object result : results) {
			String text = ((TagNode) result).getText().toString();
			Pattern p = Pattern.compile("poi: '(.+?)'");
			Matcher m = p.matcher(text);
			if (m.find()) {
				String value = m.group(1);
				try {
					Location loc = decode(value);
					shop.setLocation(loc.toString());
				} catch (Exception e) {
					Console.newLineWriteLine("%s: bad location data", shop.getUrl());
				}
				break;
			}
		}
	}
	
	private void extractBranchUrl(TagNode html, Shop shop) throws XPatherException {
		Object[] results = html.evaluateXPath("//div[@id='J_shop-branch-panel']/div[@class='title']/span[@class='title-main']/a");
		if (results.length > 0) {
			String branchUrl = ((TagNode) results[0]).getAttributeByName("href");
			shop.setBranchUrl(branchUrl);
		}
	}

	private void extractImageUrl(TagNode shopInfoNode, Shop shop) throws XPatherException {
		Object[] results = shopInfoNode.evaluateXPath("//div[@class='shop-gallery']//a[@class='thumb']/img");
		if (results.length > 0) {
			String imageUrl = ((TagNode) results[0]).getAttributeByName("src");
			if (!imageUrl.contains("shopnopic")) {
				shop.setImageUrl(imageUrl);
			}
		}
	}

	private void extractAddress(TagNode shopInfoNode, Shop shop) throws XPatherException {
		Object[] results = shopInfoNode.evaluateXPath("//div[@class='desc-list']//span[@itemprop='street-address']");
		if (results.length > 0) {
			String address = ((TagNode) results[0]).getText().toString();
			if (shop.getAddress() != null) {
				address = shop.getAddress() + address;
			}
			shop.setAddress(StringEscapeUtils.unescapeHtml4(address));
		}
	}

	private void extractDistrict(TagNode shopInfoNode, Shop shop) throws XPatherException {
		Object[] results = shopInfoNode.evaluateXPath("//div[@class='desc-list']//span[@itemprop='locality region']");
		if (results.length > 0) {
			TagNode tn = (TagNode) results[0];
			String district = tn.getText().toString();
			shop.setDistrict(district);
			tn = tn.getParent();
			String districtUrl = tn.getAttributeByName("href");
			shop.setDistrictUrl(districtUrl);
		}
	}

	private void extractPhone(TagNode shopInfoNode, Shop shop) throws XPatherException {
		Object[] results = shopInfoNode.evaluateXPath("//div[@class='desc-list']//strong[@itemprop='tel']");
		if (results.length == 0)
			results = shopInfoNode.evaluateXPath("//div[@class='desc-list']//span[@itemprop='tel']");
		if (results.length > 0) {
			List<String> list = new ArrayList<String>();
			for (Object result : results) {
				String value = ((TagNode) result).getText().toString();
				String[] phones = value.split("&nbsp;");
				list.addAll(Arrays.asList(phones));
			}
			String phone = StringUtils.join(list, ':');
			shop.setPhone(phone);
		}
	}

	private void extractAvgPrice(TagNode shopInfoNode, Shop shop) throws XPatherException {
		Object[] results = shopInfoNode.evaluateXPath("//div[@class='comment-rst']//span[@class='Price']");
		if (results.length > 0) {
			TagNode tn = (TagNode) results[0];
			String value = tn.getParent().getText().toString();
			Pattern pattern = Pattern.compile("\\d+");
			Matcher m = pattern.matcher(value);
			if (m.find()) {
				value = m.group();
				int avgPrice = Integer.parseInt(value);
				shop.setAveragePrice(avgPrice);
			}
		}
	}

	private void extractTotalComment(TagNode shopInfoNode, Shop shop) throws XPatherException {
		Object[] results = shopInfoNode.evaluateXPath("//div[@class='comment-rst']//span[@itemprop='count']");
		if (results.length > 0) {
			String value = ((TagNode) results[0]).getText().toString();
			int totalComment = Integer.parseInt(value);
			shop.setTotalComment(totalComment);
		}
	}

	private void extractMark(TagNode shopInfoNode, Shop shop) throws XPatherException {
		Object[] results = shopInfoNode.evaluateXPath("//div[@class='comment-rst']//span[@itemprop='rating']");
		if (results.length > 0) {
			String value = ((TagNode) results[0]).getAttributeByName("class");
			value = value.substring(value.lastIndexOf('r') + 1);
			int mark = Integer.parseInt(value);
			shop.setMark(mark);
		}
	}

	private void extractName(TagNode shopInfoNode, Shop shop) throws XPatherException {
		Object[] results = shopInfoNode.evaluateXPath("//div[@class='shop-name']/h1[@class='shop-title']");
		if (results.length > 0) {
			String value = ((TagNode) results[0]).getText().toString();
			shop.setName(StringEscapeUtils.unescapeHtml4(value));
		}
	}
	
	private TagNode extractTagNode(ShopData shopData) throws IOException {
		ByteArrayInputStream bytesIn = new ByteArrayInputStream(shopData.getData());
		GZIPInputStream gzipIn = new GZIPInputStream(bytesIn);
		TagNode tn = SpiderUtil.cleanInputStream(gzipIn);
		gzipIn.close();
		bytesIn.close();
		return tn;
	}
	
	public static void main(String[] args) throws IOException, XPatherException {
//		Shop shop = new Shop();
//		TagNode tn = SpiderUtil.cleanUrl("http://www.dianping.com/shop/5664332");
//		Spider.getInstance().setCurrentCityId(1);
//		new ExtractShopInfoExecutor().extractShop(tn, shop);
//		System.out.println(shop.getPhone());
		Session s = SessionManager.getInstance().openSession();
		ShopData shopData = (ShopData) s.get(ShopData.class, "/shop/5750056");
		s.close();
		ExtractShopInfoExecutor exe = new ExtractShopInfoExecutor(219);
		TagNode tn = exe.extractTagNode(shopData);
		System.out.println(tn.getText());
		Shop shop = new Shop();
		exe.extractShop(tn, shop);
		System.out.println(shop.getName());
		System.out.println(shop.getCityId());
	}

}
